#pragma once

#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/workqueue.h>

#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>

#include <media/videobuf2-v4l2.h>





/****************************
*          COMPAT           *
****************************/

#if LINUX_VERSION_CODE < KERNEL_VERSION( 5, 7, 0 )
#define VFL_TYPE_VIDEO VFL_TYPE_GRABBER
#endif

/****************************
*          DEFINE           *
****************************/

#define VIDEOBUF_QUEUE_MIN_BUF_NEED     2
#define URBSTREAM_URB_COUNT             8
#define URBSTREAM_URB_PACKETS_COUNT     32


/****************************
*           ENUM            *
****************************/


enum {
	R5U870_DI_INVALID,
	R5U870_DI_VGP_VCC2_SZ,
	R5U870_DI_VGP_VCC3,
	R5U870_DI_VGP_VCC2_AR1,
	R5U870_DI_VGP_VCC2_AR2,
	R5U870_DI_VGP_VCC5,
	R5U870_DI_VGP_VCC4,
	R5U870_DI_VGP_VCC4_VFLIP,
	R5U870_DI_VGP_VCC6,
	R5U870_DI_VGP_VCC7,
	R5U870_DI_VGP_VCC8,
	R5U870_DI_VGP_VCC9,
	R5U870_DI_HP_WEBCAM1K,
	R5U870_DI_HP_PAVWC_WDM,
	R5U870_DI_HP_PAVWC_UVC,
	R5U870_DI_HP_PAVWC_UVC_NOFW,
	R5U870_DI_GENERIC_UVC,
	R5U870_DI_FUJITSU,
};


/****************************
*          STRUCT           *
****************************/

struct urb_capsule {
	struct work_struct      workitem;
	struct urb*             urbp;
	void*                   driverInstanceThis;
};

//struct vb2_buffer must be first in struct
struct videobuffer_capsule {
	struct vb2_buffer       videobuffer;
	struct list_head        listItem;
};

struct r5u870_resolution {
	int width;              //rw_width
	int height;             //rw_height
	int requiredBandwidth;  //rw_reqbw;
	int rw_interval;
	int resolutionId;       //rw_frameidx;
};


struct r5u870_pixelformat {
	char                                description[32];
	unsigned int                        pixelformat;
	unsigned int                        flags;
	int                                 formatId;               //rp_formatidx
	const struct r5u870_resolution*     resolutionTable;        //rp_restbl
	int                                 resolutionTableAlloc;   //rp_restbl_alloc;
};


struct r5u870_model {
	char*                               modelName;            //rm_name;
	const char*                         ucodeFile;            //rm_ucode_file;
	u16                                 ucodeVersion;         //rm_ucode_version;
	const int*                          controls_wdm;         //rm_wdm_ctrlids
	const struct r5u870_pixelformat*    pixelformats;         //rm_pixfmts;
	int                                 pixelformatsCount;    //rm_npixfmts
	unsigned int                        isUvc : 1;            //rm_uvc : 1;
	unsigned int                        rm_no_ctrl_reload : 1;
	unsigned int                        rm_no_first_auto_suppress : 1;
};


struct driverInstance{
	
	struct usb_interface*   usbInterface;       //ud_intf
	struct usb_device*      usbDevice;          //ud_dev
	struct usb_driver*      usbDeviceDriver;    //um_usbdrv ?
	struct device*          dev;
	
	
	struct video_device         videoDevice;
	struct v4l2_device          v4l2Device;
	struct vb2_queue            videobuf2Queue;
	struct v4l2_ctrl_handler    webcamControlHandler;
	
	
	//this mutex used in videobuf2Queue and videoDevice
	struct mutex lock;
	
	
	struct list_head    buflist;
	spinlock_t          buflistSpinlock;
	atomic_t            buflistIsDisabled;
	
	
	const struct r5u870_model*          webcamModel;                //vh_model
	const struct r5u870_pixelformat*    webcamFormats;              //ud_fmt_array; vh_pixfmts;
	int                                 webcamFormatsCount;         //ud_fmt_array_len; vh_npixfmts;
	struct v4l2_pix_format              webcamCurrentPixelformat;   //ud_format
	const struct r5u870_pixelformat*    webcamCurrentFormat;        //vh_fmt;
	const struct r5u870_resolution*     webcamCurrentResolution;    //vh_res;
	
	
	atomic_t webcamControlRegisterIsEnabled;    //vh_ctrl_reg_enable : 1;
	atomic_t webcamIsCaptuing;                  //ud_capturing : 1,
	atomic_t usbDeviceIsDisconnected;           //ud_disconnected : 1,
	
	
	unsigned char vh_ctrl_sync : 1;
	unsigned char vh_configured : 1;
	
	
	int vh_firstframe;
	int vh_emptypkts;
	int vh_frame_accum;
	int vh_framebuf_offset;
	
	
	int         vh_ctrl_ifnum;
	int         urbstreamEndpoint;          //vh_iso_ep, us_endpoint
	int         urbstreamInterfaceNumber;   //vh_iso_ifnum
	atomic_t    urbstreamIsStreaming;       //us_streaming;
	int         urbstreamMinPacketSize;     //vh_iso_minpacket;
	int         usbCurrentAltsetting;       //vh_act_altsetting
	
	
	struct workqueue_struct*    urbstreamWorkqueue;
	struct urb_capsule          urbstreamUrbs[URBSTREAM_URB_COUNT];
	
	
	int ( *webcamApplyFormat )( struct driverInstance* );//vh_set_fmt
	int ( *vh_cap_stop )( struct driverInstance* );
	int ( *vh_decide_pkt )( struct driverInstance*, int st, int len, const u8* pktdata, int* start );
	
	
	struct list_head claimedInterfacesList;//ud_interface_list;
	
	
	/*
	//Auto settings
	int vh_auto_wb;
	int vh_aec;
	int vh_agc;
	*/
};


/****************************
*         FUNCTIONS         *
****************************/

void webcamCaptureStop( struct driverInstance* );




